#include "fiber.h"
#include "config.h"
#include "macro.h"
#include "scheduler.h"
#include <atomic>

namespace framework
{

static Logger::ptr g_logger = FRAMEWORK_LOG_NAME("system");


class MallocStackAllocator
{
public:
    static void* Alloc(size_t size)
    {
        return malloc(size);
    }

    static void Dealloc(void* vp, size_t size)
    {
        return free(vp);
    }
};
using StackAllocator = MallocStackAllocator;




static std::atomic<uint64_t> s_fiber_id {0};    //协程id
static std::atomic<uint64_t> s_fiber_count {0}; //协程数量

static thread_local Fiber* t_fiber = nullptr; //当前协程
static thread_local Fiber::ptr t_threadFiber = nullptr; //线程的主协程MainFiber


//配置协程的栈大小
static ConfigVar<uint32_t>::ptr g_fiber_stack_size = 
    Config::Lookup<uint32_t>("fiber.stack_size", 128 * 1024, "fiber stack size");


//构造每个线程的第一个协程MainFiber
//私有构造可能会被static函数调用
Fiber::Fiber()
{
    m_state = EXEC; //协程改为运行状态
    SetThis(this);  //设置当前协程是主协程

    //用于获取当前 线程/协程 的上下文信息
    if(getcontext(&m_ctx))
    {
        //返回非0调用失败，终止程序输出栈信息
        FRAMEWORK_ASSERT2(false, "getcontext");
    }

    ++s_fiber_count;

    FRAMEWORK_LOG_DEBUG(g_logger) << "Fiber::Fiber main";
}


//协程执行的函数
//协程栈大小
//是否在MainFiber上调度
Fiber::Fiber(std::function<void()> cb, size_t stacksize, bool use_caller):m_id(++s_fiber_id),m_cb(cb)
{
    //协程数加一
    ++s_fiber_count;
    
    m_stacksize = stacksize ? stacksize : g_fiber_stack_size->getValue();

    m_stack = StackAllocator::Alloc(m_stacksize);
    
    //用于获取当前 线程/协程 的上下文信息
    if(getcontext(&m_ctx))
    {
        //返回非0调用失败，终止程序输出栈信息
        FRAMEWORK_ASSERT2(false, "getcontext");
    }
    m_ctx.uc_link = nullptr;                 //此协程函数执行完成后退出
    m_ctx.uc_stack.ss_sp = m_stack;          //协程函数执行时 栈的起始地址
    m_ctx.uc_stack.ss_size = m_stacksize;    //栈大小

    if(!use_caller)
    {
        //创建协程
        makecontext(&m_ctx, &Fiber::MainFunc, 0);
    }
    else
    {
        makecontext(&m_ctx, &Fiber::CallerMainFunc, 0);
    }



    FRAMEWORK_LOG_DEBUG(g_logger) << "Fiber::Fiber id=" << m_id;
}

Fiber::~Fiber()
{
    --s_fiber_count;
    if(m_stack) //如果开辟过栈空间（走有参构造）
    {
        FRAMEWORK_ASSERT(m_state == TERM
                     || m_state == EXCEPT
                     || m_state == INIT)

        //断言协程状态 不是结束、异常、初始化 就释放空间
        StackAllocator::Dealloc(m_stack, m_stacksize);
    }
    else    //释放线程的主协程 （走默认构造）
    {
        FRAMEWORK_ASSERT(!m_cb);  //断言回调函数为空
        FRAMEWORK_ASSERT(m_state == EXEC); //断言协程状态为执行中

        Fiber* cur = t_fiber;
        if(cur == this)
        {
            SetThis(nullptr);   //将线程的主协程置空
        }
    }

    FRAMEWORK_LOG_DEBUG(g_logger) << "Fiber::~Fiber id=" << m_id
                                << " total = " << s_fiber_count;
}

//重置协程执行函数，并设置状态
//INIT, TERM, EXCEPT  ->   INIT,  
void Fiber::reset(std::function<void()> cb)
{
    FRAMEWORK_ASSERT(m_stack);  //断言栈不为空
    FRAMEWORK_ASSERT(m_state == TERM
                || m_state == EXCEPT
                || m_state == INIT);
    
    m_cb = cb;

    //重新设置协程上下文
    if(getcontext(&m_ctx))
    {
        FRAMEWORK_ASSERT2(false, "getcontext");
    }
    m_ctx.uc_link = nullptr;
    m_ctx.uc_stack.ss_sp = m_stack;
    m_ctx.uc_stack.ss_size = m_stacksize;

    //创建协程
    makecontext(&m_ctx, &Fiber::MainFunc, 0);
    m_state = INIT;
}

//将当前线程切换到执行状态
//执行的为当前线程的主协程
void Fiber::call()
{
    SetThis(this);
    m_state = EXEC; // 执行中

    //保存主协程执行上下文，并切换到指定的执行上下文
    if(swapcontext(&t_threadFiber->m_ctx, &m_ctx))   //将当前协程切换到后台
    {
        FRAMEWORK_ASSERT2(false, "swapcontext");
    }
}

//将当前协程切换到后台, 返回到线程的主协程
//执行的为当前运行协程
void Fiber::back()
{
    SetThis(t_threadFiber.get());
    if(swapcontext(&m_ctx, &t_threadFiber->m_ctx))
    {
        FRAMEWORK_ASSERT2(false, "swapcontext");
    }
}


//将当前协程切换到运行状态 主协程切换到当前协程
//getState() != EXEC  ->  getState() = EXEC
void Fiber::swapIn()
{
    SetThis(this);
    FRAMEWORK_ASSERT(m_state != EXEC); //断言当前协程不为执行状态
    m_state = EXEC;
    if(swapcontext(&Scheduler::GetMainFiber()->m_ctx, &m_ctx)) {  //将协程调度器切换到后台
        FRAMEWORK_ASSERT2(false, "swapcontext"); 
    }
}

//将当前协程切换到后台
//将当前协程切换到主协程
void Fiber::swapOut()
{
    //当前协程调度器的调度协程
    SetThis(Scheduler::GetMainFiber());
    // SetThis(t_threadFiber.get());
    if(swapcontext(&m_ctx, &Scheduler::GetMainFiber()->m_ctx))
    {
        FRAMEWORK_ASSERT2(false, "swapcontext");
    }
}


/*==================  static function  ==================*/


//设置当前线程的运行协程
void Fiber::SetThis(Fiber* f)
{
    t_fiber = f;
}

//返回当前所在协程
Fiber::ptr Fiber::GetThis()
{
    if(t_fiber)
    {
        return t_fiber->shared_from_this();
    }

    //如果t_fiber为空
    Fiber::ptr main_fiber(new Fiber);
    //默认构造设置t_fiber
    FRAMEWORK_ASSERT(t_fiber == main_fiber.get());
    t_threadFiber = main_fiber;

    return t_fiber->shared_from_this();
}

//将当前协程切换到后台,并设置为READY状态
void Fiber::YieldToReady()
{
    Fiber::ptr cur = GetThis();
    FRAMEWORK_ASSERT(cur->m_state == EXEC);
    cur->m_state = READY;
    cur->swapOut();
}

//将当前协程切换到后台,并设置为HOLD状态
void Fiber::YieldToHold()
{
    Fiber::ptr cur = GetThis();
    FRAMEWORK_ASSERT(cur->m_state == EXEC);
    cur->m_state = HOLD;
    cur->swapOut();
}

//返回当前协程的总数量
uint64_t Fiber::TotalFibers()
{
    return s_fiber_count;
}

//协程执行函数  执行完成返回到线程主协程
void Fiber::MainFunc()
{
    Fiber::ptr cur = GetThis();
    FRAMEWORK_ASSERT(cur);

    try
    {
        cur->m_cb();
        cur->m_cb = nullptr;
        cur->m_state = TERM;
    }
    catch(const std::exception& ex)
    {
        cur->m_state = EXCEPT;
        FRAMEWORK_LOG_ERROR(g_logger) << "Fiber Except:" << ex.what()
            << " fiber_id=" << cur->getId()
            << std::endl
            << framework::BacktraceToString();
    }
    catch(...)
    {
        cur->m_state = EXCEPT;
        FRAMEWORK_LOG_ERROR(g_logger) << "Fiber Except"
            << " fiber_id=" << cur->getId()
            << std::endl
            << framework::BacktraceToString();
    }
    
    //协程函数执行完毕时，释放智能指针，并将协程切换回主协程
    auto raw_ptr = cur.get();
    cur.reset();
    raw_ptr->swapOut();


    //不应该执行到这里
    FRAMEWORK_ASSERT2(false, "never reach fiber_id=" + std::to_string(raw_ptr->getId()));
}

//协程执行函数  执行完成返回到线程调度协程
void Fiber::CallerMainFunc()
{
    Fiber::ptr cur = GetThis();
    FRAMEWORK_ASSERT(cur);
    try {
        cur->m_cb();
        cur->m_cb = nullptr;
        cur->m_state = TERM;
    } catch (std::exception& ex) {
        cur->m_state = EXCEPT;
        FRAMEWORK_LOG_ERROR(g_logger) << "Fiber Except: " << ex.what()
            << " fiber_id=" << cur->getId()
            << std::endl
            << framework::BacktraceToString();
    } catch (...) {
        cur->m_state = EXCEPT;
        FRAMEWORK_LOG_ERROR(g_logger) << "Fiber Except"
            << " fiber_id=" << cur->getId()
            << std::endl
            << framework::BacktraceToString();
    }

    auto raw_ptr = cur.get();
    cur.reset();
    raw_ptr->back();
    FRAMEWORK_ASSERT2(false, "never reach fiber_id=" + std::to_string(raw_ptr->getId()));

}

//获取当前协程的id
uint64_t Fiber::GetFiberId()
{
    if(t_fiber)
    {
        return t_fiber->getId();
    }
    return 0;
}



}
